import server
import game

from boardElements import *

class GUI:
    '''
    GUI class
    '''
    def __init__(self):
        ''' 
        Basic Constructor, Starts GUI Mainloop
        '''        
        # create game object
        self.game = game.Game()
        
        # initialize Attributes
        self.initWidgets()
        
        # create Toplevel and main GUI frames
        root = Tk()
        root.title('Cathedral')
        boardFrame = Frame(root, borderwidth = SQUARE_SIZE / 2, relief = 'ridge')
        infoFrame = Frame(root)
        p2Frame = Frame(root)
        p1Frame = Frame(root)
        scoreFrame = Frame(root)
        
        # populate frames
        self.infoString = self.createInfo(infoFrame)
        self.canvas, self.widgets, self.gridLabels = self.createBoard(boardFrame)
        self.p1PieceList, self.p1Canvas = self.createPieces(p1Frame, self.game.p1Pieces)
        self.p2PieceList, self.p2Canvas = self.createPieces(p2Frame, self.game.p2Pieces)
        self.scoreString = self.createSeriesScore(scoreFrame)
        self.undoOption = self.createMenu(root)
        
        # allow for frame resizing
        root.grid_columnconfigure(0, weight = 1)
        root.grid_columnconfigure(1, weight = 1)
        root.grid_columnconfigure(2, weight = 1)
        root.grid_rowconfigure(1, weight = 1)
        
        # bind global hotkeys
        root.bind_all('<Control-z>', self.undoE)
        root.bind_all('<Control-n>', self.newE)
        root.bind_all('<Control-s>', self.saveE)
        root.bind_all('<Control-o>', self.loadE)
        root.bind_all('<Control-q>', self.quitE)
        root.bind_all('<Control-l>', self.toggleLabelsE)
        
        # grid frames
        infoFrame.grid(row = 0, column = 0, columnspan = 3, sticky = 'NSEW', pady = 2)
        p1Frame.grid(row = 1, column = 0, sticky = 'NSEW', padx = 5)
        boardFrame.grid(row = 1, column = 1, sticky = 'NSEW')
        p2Frame.grid(row = 1, column = 2, sticky = 'NSEW', padx = 5)
        scoreFrame.grid(row = 2, column = 2, sticky = 'NSEW')
        
        # center toplevel
        root.update_idletasks()
        w = root.winfo_width()
        h = root.winfo_height()
        ws = root.winfo_screenwidth()
        hs = root.winfo_screenheight()
        root.geometry('%dx%d+%d+%d' % (w, h, (ws/2) - (w/2), (hs/2) - (h/2)))
        
        # draw board widget
        self.redrawBoard(self.game.board.squares)
        
        # enter gui mainloop
        root.mainloop()
        
    def initWidgets(self):
        '''
        One-Time initialization
        '''
        self.widgets = []
        self.gridLabels = []
        self.p2PieceList = None
        self.p1PieceList = None
        self.canvas = None
        self.p2Canvas = None
        self.p1Canvas = None
        self.pieceWidth = None
        self.pieceHeight = None
        self.infoString = None
        self.scoreString = None
        self.labelsOn = False
        self.p1Color           = DEF_P1          
        self.p1TerColor        = DEF_P1_TER      
        self.p2Color           = DEF_P2           
        self.p2TerColor        = DEF_P2_TER       
        self.cathedralColor    = DEF_CATH           
        self.currentPieceColor = DEF_CURR_PIECE     
        self.conflifPieceColor = DEF_CONFLICT_PIECE 
        self.boardColor        = DEF_BOARD          
        self.p1Stipple         = DEF_P1_STIP     
        self.p1TerStipple      = DEF_P1_TER_STIP 
        self.p2Stipple         = DEF_P2_STIP      
        self.p2TerStipple      = DEF_P2_TER_STIP 
    
    def quitE(self, event):
        '''
        handels CTRL+Q keyboard event
        '''
        event.widget.quit()
    
    def saveE(self,event):
        '''
        handels CTRL+S keyboard event
        '''
        self.save()
        
    def loadE(self,event):
        '''
        handels CTRL+O keyboard event
        '''
        self.load()
    
    def newE(self,event):
        '''
        handels CTRL+N keyboard event
        '''
        self.askNewGame()
        
    def undoE(self,event):
        '''
        handels CTRL+Z keyboard event
        '''
        self.undo()
      
    def toggleLabelsE(self, event):
        '''
        handels CTRL+L keyboard event
        '''
        self.toggleLabels()  
     
    def createSeriesScore(self, root):
        '''
        Populates series score frame
        
        Returns: string object for series score
        '''
        # TODO: add 4 player capability
        string = StringVar()
        root.grid_columnconfigure(0, weight = 1)
        Label(root, textvariable = string, justify = 'left').grid(row = 0, column = 0, padx = 5, pady = 2, ipadx = 5, ipady = 2)
        string.set('Series Winner:\t%s\nCumulative Score:\t%d' % (self.game.seriesWinner, self.game.totalScore))
        
        return string        
    
    def createInfo(self, root):
        '''
        Populates Info Frame
        
        Returns: string object for current game score
        '''
        # TODO: add 4 player capability
        string = StringVar()
        root.grid_columnconfigure(1, weight = 1)
        Label(root, textvariable = string, justify = 'left', relief = 'sunken', borderwidth = 2).grid(row = 0, column = 1, padx = 5, pady = 2, ipady = 2)
        string.set('    %s : %d\t\t%s : %d    ' % (P1, self.game.p1Score, P2, self.game.p2Score))
        
        return string
    
    def createBoard(self, root):
        '''
        Populates board frame
        
        Returns: board canvas, list of rectangle widgets for board squares, list of text widgets for board labels
        '''
        widgets = []
        gridLabels = []
        canvas = Canvas(root, width = SQUARE_SIZE *  self.game.board.size, height = SQUARE_SIZE *  self.game.board.size)
        for s in self.game.board.squares:
            w = canvas.create_rectangle((s.x * SQUARE_SIZE), (s.y * SQUARE_SIZE), ((s.x * SQUARE_SIZE) + SQUARE_SIZE), ((s.y * SQUARE_SIZE) + SQUARE_SIZE), fill = 'grey')
            l = canvas.create_text((s.x * SQUARE_SIZE + SQUARE_SIZE / 2), (s.y * SQUARE_SIZE + SQUARE_SIZE / 2), text = LOC_NOT(s.x, s.y), state = 'hidden')
            widgets.append(w)
            gridLabels.append(l)
        canvas.bind('<Configure>', self.resizeCanvas)
        canvas.bind('<ButtonRelease-1>', self.placePiece)
        canvas.bind('<ButtonRelease-2>', self.rotatePiece)
        canvas.bind('<ButtonRelease-3>', self.rotatePiece)
        canvas.bind('<Motion>', self.movePiece)
        canvas.pack(expand = True, fill = 'both') 
        
        return canvas, widgets, gridLabels
    
    def reCreateBoard(self):
        '''
        Recreates the board for the current board size
        Used when sqitching between 2 / 4 player versions
        '''
        self.widgets = []
        self.gridLabels = []
        self.canvas.config(width = SQUARE_SIZE *  self.game.board.size, height = SQUARE_SIZE *  self.game.board.size)
        self.canvas.delete('all')
        for s in self.game.board.squares:
            w = self.canvas.create_rectangle((s.x * SQUARE_SIZE), (s.y * SQUARE_SIZE), ((s.x * SQUARE_SIZE) + SQUARE_SIZE), ((s.y * SQUARE_SIZE) + SQUARE_SIZE), fill = 'grey')
            l = self.canvas.create_text((s.x * SQUARE_SIZE + SQUARE_SIZE / 2), (s.y * SQUARE_SIZE + SQUARE_SIZE / 2), text = LOC_NOT(s.x, s.y), state = 'hidden')
            self.widgets.append(w)
            self.gridLabels.append(l)
        
    def resizeCanvas(self, event):
        '''
        Scales board canvas when user resizes GUI window
        '''
        xSize = event.width / self.game.board.size
        ySize = event.height / self.game.board.size
        for i in range(0, len(self.widgets)):
            s = self.game.board.squares[i]
            w = self.widgets[i]
            self.canvas.coords(w, (s.x * xSize), (s.y * ySize), ((s.x * xSize) + xSize), ((s.y * ySize) + ySize))
            l = self.gridLabels[i]
            self.canvas.coords(l, (s.x * xSize) + xSize / 2, (s.y * ySize) + ySize / 2)
            
    def rotatePiece(self, event):
        '''
        Handles piece rotation (right / middle mouse click)
        '''        
        w = self.widgets.index(self.canvas.find_closest(event.x, event.y)[0])
        self.redrawBoard(self.game.rotateCurrent(w))
        
    def movePiece(self, event):
        '''
        Handles piece movement (mouse movement)
        '''
        w = self.widgets.index(self.canvas.find_closest(event.x, event.y)[0])
        self.redrawBoard(self.game.moveCurrent(w))
            
    def placePiece(self, event):
        '''
        Handles piece placement (lect mouse click)
        '''
        w = self.widgets.index(self.canvas.find_closest(event.x, event.y)[0])
        self.redrawBoard(self.game.checkPlaceCurrent(w))
        self.confirmPlace()
        
    def confirmPlace(self):
        '''
        In network game, confirm player wants to place the last piece with promt
        '''
        if self.game.network:
            if self.game.currentPiece != None and self.game.placed:
                t = Toplevel()
                t.bind('<FocusOut>', self.confirmPlaceDecision)
                t.bind('<Key>', self.confirmPlaceDecision)
                Label(t, text = 'Are you sure?').grid(row = 0, column = 0, columnspan = 2)
                b = Button(t, text = 'Yes')
                b.bind('<ButtonRelease-1>', self.confirmPlaceDecision)
                b.grid(row = 1, column = 0)
                b = Button(t, text = 'No')
                b.bind('<ButtonRelease-1>', self.confirmPlaceDecision)
                b.grid(row = 1, column = 1)
                t.update_idletasks()
                t.focus_force()
                w = t.winfo_width()
                h = t.winfo_height()
                ws = t.winfo_screenwidth()
                hs = t.winfo_screenheight()
                t.geometry('%dx%d+%d+%d' % (w, h, (ws/2) - (w/2), (hs/2) - (h/2)))
                t.wait_window()
        else:
            self.confirmPlaceDecision(None)
                
    def confirmPlaceDecision(self, event):
        '''
        Checks if player was sure about the placement and updates board
        '''
        confirmed = not self.game.network
        try:
            confirmed = event.widget.cget('text') == 'Yes'
        except:
            try:
                confirmed = event.keysym in ['Return', 'KP_Enter']
            except:
                pass
        removedPieces = []
        if event != None:
            event.widget.winfo_toplevel().destroy()
        if confirmed and self.game.placed:
            # TODO: add 4 player capability
            if self.game.currentPiece in self.game.p1Pieces:
                self.p1PieceList.delete(int(self.p1PieceList.curselection()[0]))
            if self.game.currentPiece in self.game.p2Pieces:
                self.p2PieceList.delete(int(self.p2PieceList.curselection()[0]))
            removedPieces = self.game.placeCurrent()
        if len(self.game.board.undoPieces) > 0:
            self.undoOption.entryconfig(1, state = 'active')
        else:
            self.undoOption.entryconfig(1, state = 'disabled')
        for p in removedPieces:
            if p.name != CA[0]:
                if self.game.turn == P1:
                    self.p1PieceList.insert('end', p.name)  
                elif self.game.turn == P2:
                    self.p2PieceList.insert('end', p.name)
        self.redrawBoard(self.game.board.squares)
        if not self.game.checkAvailableMoves():
            if not self.game.firstSkip[self.game.turn]:
                self.skipTurn()
            else:
                self.game.skipTurn()
        if self.game.gameFinished():
            self.askNewGame()
    
    def skipTurn(self):
        '''
        If no valid moves, inform player 
        '''
        t = Toplevel()
        t.bind('<FocusOut>', self.turnSkipped)
        t.bind('<Key>', self.turnSkipped)
        Label(t, text = 'There are no valid moves for %s' % (self.game.turn)).grid(row = 0, column = 0)
        b = Button(t, text = 'Ok')
        b.bind('<ButtonRelease-1>', self.turnSkipped)
        b.grid(row = 1, column = 0)
        t.update_idletasks()
        t.focus_force()
        w = t.winfo_width()
        h = t.winfo_height()
        ws = t.winfo_screenwidth()
        hs = t.winfo_screenheight()
        t.geometry('%dx%d+%d+%d' % (w, h, (ws/2) - (w/2), (hs/2) - (h/2)))
        t.wait_window()
    
    def turnSkipped(self, event):
        '''
        Skip players turn
        '''
        self.game.skipTurn()
        if event != None:
            event.widget.winfo_toplevel().destroy()
        
    def redrawBoard(self, squares):
        '''
        Updates board canvas widgets and game score string
        '''
        # TODO: add 4 player functionality
        self.game.calculateScore()
        self.infoString.set('    %s : %d\t\t%s : %d    ' % (P1, self.game.p1Score, P2, self.game.p2Score))
        for i in range(0, len(self.widgets)):
            s = squares[i]
            w = self.widgets[i]
            if s.owner == P1:
                self.canvas.itemconfig(w, fill = self.p1Color, stipple = self.p1Stipple)
            elif s.owner == P1T:
                self.canvas.itemconfig(w, fill = self.p1TerColor, stipple = self.p1TerStipple)
            elif s.owner == P2:
                self.canvas.itemconfig(w, fill = self.p2Color, stipple = self.p2Stipple)
            elif s.owner == P2T:
                self.canvas.itemconfig(w, fill = self.p2TerColor, stipple = self.p2TerStipple)
            elif s.owner == C:
                self.canvas.itemconfig(w, fill = self.cathedralColor, stipple = '')
            elif s.owner == P:
                self.canvas.itemconfig(w, fill = self.currentPieceColor, stipple = '')
            elif s.owner == O:
                self.canvas.itemconfig(w, fill = self.conflifPieceColor, stipple = '')
            else:
                self.canvas.itemconfig(w, fill = self.boardColor, stipple = '')
 
    def createMenu(self, root):
        '''
        Creates menubar
        '''
        # TODO: add rule change dialog
        menubar = Menu(root)
        
        filemenu = Menu(menubar, tearoff = 0)
        filemenu.add_command(label = "New Game", command = self.askNewGame, accelerator="Ctrl+N")
        filemenu.add_command(label = "Save", command = self.save, accelerator="Ctrl+S")
        filemenu.add_command(label = "Open", command = self.load, accelerator="Ctrl+O")
        filemenu.add_separator()
        filemenu.add_command(label = "Exit", command = root.quit, accelerator="Ctrl+Q")
        
        gamemenu = Menu(menubar, tearoff = 0)
        gamemenu.add_command(label = "Concede Game", command = self.askConcede)
        gamemenu.add_command(label = "Undo Last Move", command = self.undo, accelerator="Ctrl+Z", state = 'disabled')
        gamemenu.add_command(label = "Toggle Grid Labels", command = self.toggleLabels, accelerator="Ctrl+L")
        gamemenu.add_command(label = "Choose Color Scheme", command = self.chooseColors)
        
        helpmenu = Menu(menubar, tearoff = 0)
        helpmenu.add_command(label = "How To Play...", command = None)
        helpmenu.add_command(label = "About", command = None)
        
        menubar.add_cascade(label = "File", menu = filemenu)
        menubar.add_cascade(label = "Game", menu = gamemenu)
        menubar.add_cascade(label = "Help", menu = helpmenu)
        
        root.config(menu=menubar)
        
        return gamemenu
    
    def chooseColors(self):
        '''
        color change dialog
        '''
        self.rgb = None
        self.currentColorSelect = None
        self.currentColorLabel = None
        self.selectedTexture = None
        
        t = Toplevel()
        
        f = Frame(t)
        self.currentColorSelect = StringVar()
        self.currentColorSelect.set(P1)
        Radiobutton(f, text = 'Player 1 Piece Color',     variable = self.currentColorSelect, command = self.selectColor, value = P1).grid(row = 0, column = 0, sticky = 'W')
        Radiobutton(f, text = 'Player 1 Territory Color', variable = self.currentColorSelect, command = self.selectColor, value = P1T).grid(row = 1, column = 0, sticky = 'W')
        Radiobutton(f, text = 'Player 2 Color',           variable = self.currentColorSelect, command = self.selectColor, value = P2).grid(row = 2, column = 0, sticky = 'W')
        Radiobutton(f, text = 'Player 2 Territory Color', variable = self.currentColorSelect, command = self.selectColor, value = P2T).grid(row = 3, column = 0, sticky = 'W')
        Radiobutton(f, text = 'Cathedral Color',          variable = self.currentColorSelect, command = self.selectColor, value = C).grid(row = 4, column = 0, sticky = 'W')
        Radiobutton(f, text = 'Current Piece Color',      variable = self.currentColorSelect, command = self.selectColor, value = P).grid(row = 5, column = 0, sticky = 'W')
        Radiobutton(f, text = 'Piece Confilct Color',     variable = self.currentColorSelect, command = self.selectColor, value = O).grid(row = 6, column = 0, sticky = 'W')
        Radiobutton(f, text = 'Board Color',              variable = self.currentColorSelect, command = self.selectColor, value = NT).grid(row = 7, column = 0, sticky = 'W')
        f.grid(row = 0, column = 0, padx = 10)
        
        f = Frame(t)
        self.rgb = (IntVar(), IntVar(), IntVar())
        self.selectedTexture = StringVar()
        self.selectedTexture.set('')
        f.grid_rowconfigure(0, weight = 1)
        f.grid_columnconfigure(0, weight = 1)
        
        f0 = Frame(f)
        self.currentColorLabel = Canvas(f0, height = 100, width = 100)
        self.currentColorLabel.create_rectangle(0, 0, 200, 200)
        self.currentColorLabel.grid(row = 0, column = 0, sticky = 'NSWE')
        f0.grid(row = 0, column = 0)
        
        f1 = Frame(f)
        Label(f1, text = 'Red').grid(row = 0, column = 1, sticky = 'W', padx = 10)
        Label(f1, text = 'Green').grid(row = 1, column = 1, sticky = 'W', padx = 10)
        Label(f1, text = 'Blue').grid(row = 2, column = 1, sticky = 'W', padx = 10)
        Scale(f1, orient = 'horizontal', length = 200, from_ = 0, to = 255, variable = self.rgb[0], command = self.colorChanged).grid(row = 0, column = 2)
        Scale(f1, orient = 'horizontal', length = 200, from_ = 0, to = 255, variable = self.rgb[1], command = self.colorChanged).grid(row = 1, column = 2)
        Scale(f1, orient = 'horizontal', length = 200, from_ = 0, to = 255, variable = self.rgb[2], command = self.colorChanged).grid(row = 2, column = 2)
        f1.grid(row = 0, column = 1, padx = 10)
        
        f2 = Frame(f)
        Radiobutton(f2, text = 'No Stipple', variable = self.selectedTexture, command = self.textureChanged, value = '').grid(row = 0, column = 0, sticky = 'W')
        Radiobutton(f2, text = '75% Stipple', variable = self.selectedTexture, command = self.textureChanged, value = 'gray75').grid(row = 3, column = 0, sticky = 'W')
        Radiobutton(f2, text = '50% Stipple', variable = self.selectedTexture, command = self.textureChanged, value = 'gray50').grid(row = 2, column = 0, sticky = 'W')
        Radiobutton(f2, text = '25% Stipple', variable = self.selectedTexture, command = self.textureChanged, value = 'gray25').grid(row = 1, column = 0, sticky = 'W')
        f2.grid(row = 0, column = 2)
        
        f.grid(row = 0, column = 1, padx = 10)
        
        f = Frame(t)
        b = Button(f, text = 'Ok')
        b.bind('<ButtonRelease-1>', self.closeColors)
        b.grid(row = 0, column = 0)
        b = Button(f, text = 'Cancel')
        b.bind('<ButtonRelease-1>', self.closeColors)
        b.grid(row = 0, column = 1)
        Button(f, text = 'Reset', command = self.resetColors).grid(row = 0, column = 3, padx = 30)        
        f.grid(row = 1, column = 0, columnspan = 2)
        
        self.selectColor()
        
        t.update_idletasks()
        t.focus_force()
        w = t.winfo_width()
        h = t.winfo_height()
        ws = t.winfo_screenwidth()
        hs = t.winfo_screenheight()
        t.geometry('%dx%d+%d+%d' % (w, h, (ws/2) - (w/2), (hs/2) - (h/2)))
        t.wait_window()
    
    def selectColor(self):   
        '''
        Selects which color to change
        '''
        texture = ''
        if self.currentColorSelect.get() == P1:
            color = self.p1Color
            texture = self.p1Stipple
        elif self.currentColorSelect.get() == P1T:
            color = self.p1TerColor
            texture = self.p1TerStipple
        elif self.currentColorSelect.get() == P2:
            color = self.p2Color
            texture = self.p2Stipple
        elif self.currentColorSelect.get() == P2T:
            color = self.p2TerColor
            texture = self.p2TerStipple
        elif self.currentColorSelect.get() == C:
            color = self.cathedralColor
        elif self.currentColorSelect.get() == P:
            color = self.currentPieceColor
        elif self.currentColorSelect.get() == O:
            color = self.conflifPieceColor
        elif self.currentColorSelect.get() == NT:
            color = self.boardColor
        self.currentColorLabel.itemconfig(self.currentColorLabel.find_all()[0], fill = color, stipple = texture)
        red = int(color[1:3], 16)
        green = int(color[3:5], 16)
        blue = int(color[5:7], 16)
        self.rgb[0].set(red)
        self.rgb[1].set(green)
        self.rgb[2].set(blue)
        self.selectedTexture.set(texture)
        
    
    def resetColors(self):
        '''
        set all colors to defaults (defined in pieceConstants.py)
        '''
        self.p1Color        = DEF_P1         
        self.p1TerColor     = DEF_P1_TER      
        self.p2Color         = DEF_P2           
        self.p2TerColor      = DEF_P2_TER       
        self.cathedralColor    = DEF_CATH           
        self.currentPieceColor = DEF_CURR_PIECE     
        self.conflifPieceColor = DEF_CONFLICT_PIECE 
        self.boardColor        = DEF_BOARD          
        self.p1Stipple      = DEF_P1_STIP     
        self.p1TerStipple   = DEF_P1_TER_STIP 
        self.p2Stipple       = DEF_P2_STIP      
        self.p2TerStipple    = DEF_P2_TER_STIP 
        self.selectColor()
        self.redrawBoard(self.game.board.squares)
    
    def textureChanged(self):
        '''
        handles radio button event and calls colorChanged
        '''
        self.colorChanged(None)
    
    def colorChanged(self, event):
        '''
        changes selected color when RGB or texture changes
        '''
        red = self.rgb[0].get()
        green = self.rgb[1].get()
        blue = self.rgb[2].get()
        color = GET_COLOR(red, green, blue)
        texture = self.selectedTexture.get()
        if self.currentColorSelect.get() == P1:
            self.p1Color = color
            self.p1Stipple = texture
        elif self.currentColorSelect.get() == P1T:
            self.p1TerColor = color
            self.p1TerStipple = texture
        elif self.currentColorSelect.get() == P2:
            self.p2Color = color
            self.p2Stipple = texture
        elif self.currentColorSelect.get() == P2T:
            self.p2TerColor = color
            self.p2TerStipple = texture
        elif self.currentColorSelect.get() == C:
            self.cathedralColor = color
            self.selectedTexture.set('')
        elif self.currentColorSelect.get() == P:
            self.currentPieceColor = color
            self.selectedTexture.set('')
        elif self.currentColorSelect.get() == O:
            self.conflifPieceColor = color
            self.selectedTexture.set('')
        elif self.currentColorSelect.get() == NT:
            self.boardColor = color
            self.selectedTexture.set('')
        texture = self.selectedTexture.get()
        self.currentColorLabel.itemconfig(self.currentColorLabel.find_all()[0], fill = color, stipple = texture)
        self.redrawBoard(self.game.board.squares)
    
    def closeColors(self, event):
        '''
        accepts or cancels changes to color scheme
        '''
        self.rgb = None
        self.currentColorSelect = None
        self.currentColorLabel = None
        self.selectedTexture = None
        try:
            button = event.widget.cget('text')
            if button != 'Ok':
                self.p1Color        = DEF_P1          
                self.p1TerColor     = DEF_P1_TER      
                self.p2Color         = DEF_P2           
                self.p2TerColor      = DEF_P2_TER       
                self.cathedralColor    = DEF_CATH           
                self.currentPieceColor = DEF_CURR_PIECE     
                self.conflifPieceColor = DEF_CONFLICT_PIECE 
                self.boardColor        = DEF_BOARD          
                self.p1Stipple      = DEF_P1_STIP     
                self.p1TerStipple   = DEF_P1_TER_STIP 
                self.p2Stipple       = DEF_P2_STIP      
                self.p2TerStipple    = DEF_P2_TER_STIP
                self.redrawBoard(self.board.squares)
        except:
           pass
        event.widget.winfo_toplevel().destroy()
        
    def save(self):
        '''
        Saves current game to a file using pickle
        '''
        saveFile = tkFileDialog.asksaveasfile(filetypes = [('Cathedral Saved Game','*.cat')], initialdir = './Save', defaultextension = '.cat', mode = 'wb')
        if saveFile != None:
            saveFile.write(self.game.pickleObjects())
            saveFile.close()
                
    def load(self):
        '''
        loads a game from a file using pickle
        '''
        #TODO: add 4 player functionality
        loadFile = tkFileDialog.askopenfile(filetypes = [('Cathedral Saved Game','*.cat')], initialdir = './Save', defaultextension = '.cat', mode = 'rb')
        if loadFile != None:      
            string = ''
            for line in loadFile.readlines():
                string = string + line
            self.game.unPickleObjects(string)
            loadFile.close()
        self.redrawBoard(self.game.board.squares)
        self.drawPiece(self.p2Canvas, self.game.p1Selection)
        self.drawPiece(self.p1Canvas, self.game.p2Selection)
        self.scoreString.set('Series Winner:      %s\nCumulative Score:   %d' % (self.game.seriesWinner, abs(self.game.totalScore)))
    
    def askNewGame(self):
        '''
        prompts user to chose what kind of game to start
        '''
        string = 'Start New Game?'
        if self.game.started:
            winner, margin = self.game.getWinner()
            if winner != TIE:
                string = 'Start New Game?\n%s will win by %d points' % (winner, margin)
        t = Toplevel()
        t.bind('<FocusOut>', self.confirmNewGame)
        Label(t, text = string).grid(row = 0, column = 0, columnspan = 3)
        b = Button(t, text = 'Local')
        b.bind('<ButtonRelease-1>', self.confirmNewGame)
        b.grid(row = 1, column = 0)
        b = Button(t, text = 'Network')
        b.bind('<ButtonRelease-1>', self.confirmNewGame)
        b.grid(row = 1, column = 1)
        b = Button(t, text = '4 Player Local')
        b.bind('<ButtonRelease-1>', self.confirmNewGame)
        b.grid(row = 1, column = 2)
        b = Button(t, text = 'Cancel')
        b.bind('<ButtonRelease-1>', self.confirmNewGame)
        b.grid(row = 1, column = 3)
        t.update_idletasks()
        t.focus_force()
        w = t.winfo_width()
        h = t.winfo_height()
        ws = t.winfo_screenwidth()
        hs = t.winfo_screenheight()
        t.geometry('%dx%d+%d+%d' % (w, h, (ws/2) - (w/2), (hs/2) - (h/2)))
        t.wait_window()
    
    def confirmNewGame(self, event):
        '''
        handles player choice for new game
        '''
        try:
            button = event.widget.cget('text')
            event.widget.winfo_toplevel().destroy()
        except:
            pass
        self.game.newGame(button)  
        self.reCreateBoard()          
        self.redrawBoard(self.game.board.squares)
        self.p1PieceList.delete(0, 'end')
        self.p2PieceList.delete(0, 'end')
        for piece in self.game.p1Pieces:
            self.p1PieceList.insert('end', piece.name)  
        for piece in self.game.p2Pieces:
            self.p2PieceList.insert('end', piece.name)  
        # TODO: add 4 player functionality
        if self.game.turn == P1:
            self.drawPiece(self.p1Canvas, self.game.currentPiece)
        if self.game.turn == P2:
            self.drawPiece(self.p2Canvas, self.game.currentPiece)
    
    def askConcede(self):
        '''
        prompts user if they want to concede (and therefore end) the current game
        '''
        if self.game.started:
            winner, margin = self.game.getWinner()
            t = Toplevel()
            t.bind('<FocusOut>', self.confirmConcede)
            if winner != TIE:
                string = 'Stop playing current game?\n%s will win by %d points' % (winner, margin)
            else:
                string = 'Stop playing current game?'
            Label(t, text = string).grid(row = 0, column = 0, columnspan = 2)
            b = Button(t, text = 'Yes')
            b.bind('<ButtonRelease-1>', self.confirmConcede)
            b.grid(row = 1, column = 0)
            b = Button(t, text = 'No')
            b.bind('<ButtonRelease-1>', self.confirmConcede)
            b.grid(row = 1, column = 1)
            t.update_idletasks()
            t.focus_force()
            w = t.winfo_width()
            h = t.winfo_height()
            ws = t.winfo_screenwidth()
            hs = t.winfo_screenheight()
            t.geometry('%dx%d+%d+%d' % (w, h, (ws/2) - (w/2), (hs/2) - (h/2)))
            t.wait_window()
    
    def confirmConcede(self, event):
        '''
        handles player choice for conceding the game
        '''
        try:
            button = event.widget.cget('text')
            if button == 'Yes':  
                self.gameOver()                
        except:
            pass
        event.widget.winfo_toplevel().destroy()
            
    def undo(self):
        '''
        undo's last move as long as a piece wasn't captured        
        '''
        piece = self.game.undo()
        if piece != None:
            if piece.owner == P1:
                self.p1PieceList.insert('end', piece.name) 
            if piece.owner == P2:
                self.p2PieceList.insert('end', piece.name) 
            if piece.owner == P3:
                self.p3PieceList.insert('end', piece.name) 
            if piece.owner == P4:
                self.p4PieceList.insert('end', piece.name) 
        self.redrawBoard(self.game.board.squares)   
        if len(self.game.board.undoPieces) > 0:
            self.undoOption.entryconfig(1, state = 'active')
        else:
            self.undoOption.entryconfig(1, state = 'disabled')
            
    def toggleLabels(self):
        '''
        toggles display of board coordinates
        '''
        if self.labelsOn:
            self.labelsOn = False
            state = 'hidden'
        else:
            self.labelsOn = True
            state = 'normal'
        for l in self.gridLabels:
            self.canvas.itemconfig(l, state = state)
            
    def createPieces(self, root, pieces):
        '''
        Populates piece frames
        
        Returns: piece list object, piece canvas for displaying the selected piece
        '''
        menuFrame = Frame(root)
        scrollbar = Scrollbar(menuFrame, orient = 'vertical')
        menu = Listbox(menuFrame, yscrollcommand = scrollbar.set, height = 14, exportselection = 0)
        scrollbar.config(command = menu.yview)
        scrollbar.pack(side = 'right', fill = 'y')
        menu.pack(side = 'left', fill = 'both', expand = True)
        for p in pieces:
            menu.insert('end', p.name)            
        menu.bind('<ButtonRelease-1>', self.selectPiece)
        menuFrame.grid(row = 0, column = 0, sticky = 'NSWE')
        
        self.pieceWidth = SQUARE_SIZE * 4 + 2
        self.pieceHeight = SQUARE_SIZE * 4 + 2
        
        canvas = Canvas(root, width = self.pieceWidth, height = self.pieceHeight)
        canvas.grid(row = 1, column = 0, padx = 20, pady = 20, sticky = 'NSEW')
        canvas.bind('<Configure>', self.resizePieces)
        
        root.grid_columnconfigure(0, weight = 1)
        root.grid_rowconfigure(0, weight = 1)
        root.grid_rowconfigure(1, weight = 1)
        
        return menu, canvas
    
    def selectPiece(self, event):
        '''
        handles user pice selection and displays selected piece
        '''
        if len(event.widget.get(0, 'end')) != 0:
            index = int(event.widget.curselection()[0])
            piece = event.widget.get(index)
            try: 
                canPick = (self.game.currentPiece.name != CA[0])
            except:
                canPick = True
            if canPick:
                if event.widget == self.p1PieceList:
                    self.game.selectPiece(P1, piece)
                    self.drawPiece(self.p1Canvas, self.game.p1Selection)
                elif event.widget == self.p2PieceList:
                    self.game.selectPiece(P2, piece)
                    self.drawPiece(self.p2Canvas, self.game.p2Selection)
        
    def drawPiece(self, canvas, newPiece):
        '''
        draws selected piece 
        '''
        if newPiece != None:
            xSize = self.pieceWidth / 4 - 1
            ySize = self.pieceHeight / 4 - 1
            geometry = newPiece.geometry
            for w in canvas.find_all():
                canvas.delete(w)
            minX = 0
            minY = 0
            if newPiece.owner == C:
                fill = self.cathedralColor
            elif newPiece.owner == P1:
                fill = self.p1Color
            elif newPiece.owner == P2:
                fill = self.p2Color
            else:
                fill = 'grey'
            for (x, y) in geometry:
                minX = min(x, minX)
                minY = min(y, minY)
            for (x, y) in geometry:
                canvas.create_rectangle((x - minX) * xSize + 1, (y - minY) * ySize + 1, (x - minX) * xSize + xSize + 1, (y - minY) * ySize + ySize + 1, fill = fill)
    
    def resizePieces(self, event):
        '''
        resizes selected piece when user resizes GUI
        '''
        #TODO: add 4 player functionality
        self.pieceWidth = event.width
        self.pieceHeight = event.height
        if event.widget == self.p1Canvas:
            self.drawPiece(event.widget, self.game.p1Selection)
        elif event.widget == self.p2Canvas:
            self.drawPiece(event.widget, self.game.p2Selection)
    
    def gameOver(self):
        '''
        ends game and updates series score
        '''
        self.game.gameOver()
        self.scoreString.set('Series Winner:      %s\nCumulative Score:   %d' % (self.seriesWinner, abs(self.totalScore)))
        
if __name__ == '__main__':
    '''
    creates GUI when run from command line
    '''
    g = GUI()